package com.yudean.esb.dao.esb;

import com.yudean.esb.domain.esb.EsbRecvServiceMapper;
import com.yudean.esb.domain.esb.EsbRecvServiceTempData;
import com.yudean.esb.mapper.esb.EsbRecvServiceTempDataMapper;
import com.yudean.esb.util.IDGenerator;
import com.yudean.esb.util.SpringUtil;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.text.SimpleDateFormat;
import java.util.*;

@Repository
@Scope("prototype")
public class EsbRecvServiceTempDataDao {

    private static final Logger logger = LoggerFactory.getLogger(EsbRecvServiceTempDataDao.class);
    private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    @Resource
    private EsbRecvServiceTempDataMapper esbRecvServiceTempDataMapper;
    @Resource
    private EsbRecvServiceMapperDao esbRecvServiceMapperDao;
    @Resource
    private DataSource dataSource;


    public static int BATCH_SIZE = 20; //批量提交大小

    /**
     * 用来检查法务系统的临时数据是否存在，根据表的controlID
     *
     * @param controlId 控制段
     * @return count的个数
     */
    public long countFwsControlId(String controlId) {
        return esbRecvServiceTempDataMapper.countFwsControlId(controlId);
    }

    /**
     * 读取法务系统相关的数据，根据控制ID
     *
     * @param controlId 控制ID
     * @return
     */
    public List<Map<String, Object>> selectFwsPkgById(String controlId) {

        return esbRecvServiceTempDataMapper.selectFwsByPkgId(controlId);
    }

    public long count(String transactionID) {
        return esbRecvServiceTempDataMapper.count(transactionID);
    }

    public boolean insert(EsbRecvServiceTempData data) {
        boolean result = false;
        try {
            esbRecvServiceTempDataMapper.insert(data);
            result = true;
        } catch (Exception e) {
            logger.error("EsbRecvServiceMapperDao insert():e-{}", e.getMessage());
            result = false;
        }
        return result;
    }

    /**
     * 插入数据，注意，此处map的key需要保持大写（ATTRIBUTE）
     *
     * @param data 数据
     * @return 是否插入成功
     */
    public boolean insertWithMapKeyInUpperCase(EsbRecvServiceTempData data) {
        boolean result = false;
        try {
            esbRecvServiceTempDataMapper.insertWithMapKeyInUpperCase(data);
            result = true;
        } catch (Exception e) {
            logger.error("EsbRecvServiceMapperDao insert():e-{}", e.getMessage());
            result = false;
        }
        return result;
    }

    /**
     * 根据服务字段映射,将节点名映射为中间字段名
     *
     * @param serviceID
     * @param requestList
     * @return
     */
    private ArrayList<Map<String, Object>> transformToTempData(String serviceID, List<Map<String, Object>> requestList) {
        //TODO 转换数据结构
        ArrayList<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
        for (Map<String, Object> map : requestList) {
            //先创建100个attribute键，值为null
            HashMap<String, Object> data = new HashMap<String, Object>();
            for (int i = 0; i < 100; i++) {
                String key = "attribute" + i;
                String value = null;
                data.put(key, value);
            }
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String node = iterator.next();
                String value = (String) map.get(node);
                //如果节点名为operation，则不用映射为中间字段
                if ("operation".equals(node)) {
                    data.put(node, value);
                    continue;
                }
                EsbRecvServiceMapper mapper = new EsbRecvServiceMapper();
                mapper.setServiceID(serviceID);
                mapper.setNode(node);
                EsbRecvServiceMapper result = null;
                if (value != null) {
                    result = esbRecvServiceMapperDao.get(mapper);
                    if (result == null) {
                        logger.error("data node:{} has no temp attribute in RecvServiceMapper", node);
                        continue;
                    }
                    String newKey = result.getTempName();
                    String type = result.getDataType();
                    if (newKey == null) {
                        logger.error("No temp attribute name for node:{}", node);
                        continue;
                    }
                    //时间处理
                    if ("time".equals(type)) {
                        try {
                            Long timeVal = Long.parseLong(value);
                            Date time = new Date(timeVal);
                            value = SIMPLE_DATE_FORMAT.format(time);
                        } catch (Exception ex) {
                            logger.error("转存数据，毫秒时间戳格式转换失败err={},尝试新方案！", ex.getMessage(), ex.getCause());
                            try {
                                SIMPLE_DATE_FORMAT.parse(value);
                            } catch (Exception err) {
                                logger.error("转存数据，格式={}，转换失败err={}", SIMPLE_DATE_FORMAT, err.getMessage(), err.getCause());
                                value = null;
                            }
                        }
                        //数字类型
                    } else if ("number".equals(type)) {
                        if ("".equals(value)) {
                            value = null;
                        } else {
                            try {
                                Double timeVal = Double.parseDouble(value);
                            } catch (Exception ex) {
                                logger.error("尝试转换数字类型数据，err={},", ex.getMessage(), ex.getCause());
                                value = null;
                            }
                        }
                    }
                    data.put(newKey, value);
                }
            }
            dataList.add(data);
        }
        return dataList;
    }

    /**
     * 批量插入临时表数据
     *
     * @param serviceID
     * @param transactionID
     * @param pkg
     * @param requestList
     * @return
     */
    public boolean batchInsert(String serviceID, String transactionID, String pkg,
                               List<Map<String, Object>> requestList) {
        ArrayList<Map<String, Object>> dataList = transformToTempData(serviceID, requestList);
        logger.debug("insert():request-{serviceID:{},transactionID:{},pkg:{},requestList size:{}}", serviceID, transactionID, pkg, requestList.size());

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        SqlSessionFactory sessionFactory = (SqlSessionFactory) SpringUtil.getBean("sqlSessionFactory");
        SqlSession sqlSession = sessionFactory.openSession(ExecutorType.REUSE, false);
        DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
        TransactionStatus status = txManager.getTransaction(def);
        EsbRecvServiceTempDataMapper mapper = null;

        //解决Oracle批量插入时所有列数不能超过1000，这里采用分批插入
        int dataSize = dataList.size();
        int round = dataSize / BATCH_SIZE + 1;
        if (dataSize % BATCH_SIZE == 0) {
            round = round - 1;
        }
        for (int i = 0, s = 0; i < round; i++, s += BATCH_SIZE) {
            int toIndex = s + BATCH_SIZE;
            if (i == round - 1) {
                toIndex = dataSize;
            }
            List<Map<String, Object>> sublist = dataList.subList(s, toIndex);
            EsbRecvServiceTempData tempData = new EsbRecvServiceTempData();
            tempData.setID(IDGenerator.getHex());
            tempData.setTransactionID(transactionID);
            tempData.setPackageID(pkg);
            tempData.setData(sublist);
            try {
                mapper = sqlSession.getMapper(EsbRecvServiceTempDataMapper.class);
                mapper.insert(tempData);
            } catch (Exception e) {
                logger.error("insertBatch() insert error:{}", e.getMessage());
                txManager.rollback(status);
                sqlSession.close();
            }
        }
        boolean isConflict = checkPrimaryKeyConflict(serviceID, transactionID, pkg);
        if (isConflict) {
            txManager.rollback(status);
            sqlSession.close();
        } else {
            txManager.commit(status);
            sqlSession.close();
        }
        return !isConflict;
    }

    /**
     * 批量插入临时表数据
     *
     * @param serviceID
     * @param transactionID
     * @param pkg
     * @param forceReplace
     * @param requestList
     * @return
     */
    public synchronized boolean batchInsert(String serviceID, String transactionID, String pkg, String forceReplace,
                                            ArrayList<Map<String, Object>> requestList) {
        //转换映射关系
        ArrayList<Map<String, Object>> dataList = transformToTempData(serviceID, requestList);
        logger.debug("batchInsert():request-{serviceID:{},transactionID:{},pkg:{},forceReplace:{},requestList size:{}}",
                serviceID, transactionID, pkg, forceReplace, requestList.size());

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        SqlSessionFactory sessionFactory = (SqlSessionFactory) SpringUtil.getBean("sqlSessionFactory");
        SqlSession sqlSession = sessionFactory.openSession(ExecutorType.REUSE, false);
        DataSourceTransactionManager txManager = new DataSourceTransactionManager(dataSource);
        TransactionStatus status = txManager.getTransaction(def);
        EsbRecvServiceTempDataMapper mapper = null;

        //解决Oracle批量插入时所有列数不能超过1000，这里采用分批插入
        int dataSize = dataList.size();
        int round = dataSize / BATCH_SIZE + 1;
        if (dataSize % BATCH_SIZE == 0) {
            round = round - 1;
        }
        for (int i = 0, s = 0; i < round; i++, s += BATCH_SIZE) {
            int toIndex = s + BATCH_SIZE;
            if (i == round - 1) {
                toIndex = dataSize;
            }
            List<Map<String, Object>> sublist = dataList.subList(s, toIndex);
            EsbRecvServiceTempData tempData = new EsbRecvServiceTempData();
            tempData.setID(IDGenerator.getHex());
            tempData.setTransactionID(transactionID);
            tempData.setPackageID(pkg);
            tempData.setData(sublist);
            try {
                mapper = sqlSession.getMapper(EsbRecvServiceTempDataMapper.class);
                if (forceReplace != null && forceReplace.equals("1")) {
                    mapper.delete(tempData);
                }
                mapper.insert(tempData);
            } catch (Exception e) {
                logger.error("batchInsert() insert error:{}", e.getMessage());
                txManager.rollback(status);
                sqlSession.close();
                return false;
            }
        }
        boolean isConflict = checkPrimaryKeyConflict(serviceID, transactionID, pkg);
        if (isConflict) {
            txManager.rollback(status);
            sqlSession.close();
        } else {
            txManager.commit(status);
            sqlSession.close();
        }
        return !isConflict;
    }

    /**
     * 检查主键冲突
     */
    private boolean checkPrimaryKeyConflict(String serviceID, String transactionID, String pkg) {
        boolean isConflict = false;
        EsbRecvServiceMapper filter = new EsbRecvServiceMapper();
        filter.setServiceID(serviceID);
        filter.setIsPrimary("1");
        List<EsbRecvServiceMapper> mapperList = esbRecvServiceMapperDao.getPrimaryKey(filter);
        if (mapperList == null || mapperList.size() <= 0) {
            return isConflict;
        }
        List<Map<String, String>> conflictPair = selectConflict(mapperList, serviceID);
        if (conflictPair != null && conflictPair.size() > 0) {
            for (Map<String, String> map : conflictPair) {
                if (map == null) {  //如果找不到主键重复，会返回一个size=1的元素为null的list
                    break;
                } else {
                    isConflict = true;
                    logger.error("serviceID:{},transactionID:{},pkg:{} 存在主键冲突!", serviceID, transactionID, pkg);
                    for (Map.Entry<String, String> entry : map.entrySet()) {
                        logger.error("冲突主键:{},键值:{}", entry.getKey(), entry.getValue());
                    }
                }

            }
        }
        return isConflict;
    }

    public List<Map<String, String>> selectConflict(List<EsbRecvServiceMapper> list, String serviceID) {
        return esbRecvServiceTempDataMapper.selectConflict(list, serviceID);
    }

    public void delete(EsbRecvServiceTempData tempData) {
        esbRecvServiceTempDataMapper.delete(tempData);
    }
}
